class Api {
    constructor(baseUrl, headers = {}, maxConcurrentRequests = 5) {
        this.baseUrl = baseUrl;
        this.headers = {
            'Content-Type': 'application/json',
            ...headers,
        };
        this.requestInterceptors = [];
        this.responseInterceptors = [];
        this.maxConcurrentRequests = maxConcurrentRequests;
        this.currentRequests = 0;
        this.requestQueue = [];
        this.cache = new Map();
        this.abortControllers = new Map();
    }

    async request(endpoint, method, body = null, credentials = 'omit', timeout = 10000, retries = 3, useCache = false, customConfig = {}) {
        const cacheKey = `${method}:${endpoint}`;

        // 使用缓存
        if (useCache && this.cache.has(cacheKey)) {
            return this.cache.get(cacheKey);
        }

        // 并发控制
        if (this.currentRequests >= this.maxConcurrentRequests) {
            await new Promise((resolve) => this.requestQueue.push(resolve));
        }

        this.currentRequests++;

        let lastError;
        for (let i = 0; i < retries; i++) {
            try {
                const result = await this._request(endpoint, method, body, credentials, timeout, customConfig);
                if (useCache) {
                    this.cache.set(cacheKey, result);
                }
                return result;
            } catch (error) {
                lastError = error;
                if (i === retries - 1) {
                    throw lastError;
                }
            }
        }
    }

    /**
     * 内部请求方法
     * @param {string} endpoint - 请求的端点
     * @param {string} method - HTTP 方法
     * @param {Object} body - 请求体
     * @param {string} credentials - 认证方式
     * @param {number} timeout - 超时时间
     * @param {Object} customConfig - 自定义配置
     * @returns {Promise<any>} - 返回响应内容
     */
    async _request(endpoint, method, body, credentials, timeout, customConfig = {}) {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);

        // 存储 controller 以便后续取消
        this.abortControllers.set(endpoint, controller);

        let config = {
            method,
            headers: this.headers,
            credentials,
            signal: controller.signal,
            ...customConfig,
        };

        if (body) {
            config.body = JSON.stringify(body);
        }

        // 执行请求拦截器
        for (let interceptor of this.requestInterceptors) {
            config = await interceptor(config, endpoint, method);
        }

        const url = `${this.baseUrl}${endpoint}`;
        let response;
        try {
            response = await fetch(url, config);
            if (!response.ok) {
                throw new ApiError(`HTTP 错误! 状态码: ${response.status}`, response.status, response, config);
            }
        } catch (error) {
            console.error('请求失败:', error);
            throw error;
        } finally {
            clearTimeout(timeoutId);
            this.abortControllers.delete(endpoint);
            this.currentRequests--;
            if (this.requestQueue.length > 0) {
                this.requestQueue.shift()();
            }
        }

        // 响应的数据
        let data = null

        // 根据 responseType 返回相应类型的数据
        if (customConfig.responseType === 'text') {
            data = await response.text();
        } else if (customConfig.responseType === 'blob') {
            data = await response.blob();
        } else {
            data = await response.json(); // 默认返回 JSON
        }

        // 执行响应拦截器
        for (let interceptor of this.responseInterceptors) {
            data = await interceptor(response, data);
        }

        // 返回最终的data
        return data
    }

    async get(endpoint, params = {}, credentials = 'omit', timeout = 10000, retries = 3, useCache = false) {
        let fullEndpoint = endpoint;
        if (Object.keys(params).length > 0) {
            const queryString = new URLSearchParams(params).toString();
            fullEndpoint += `?${queryString}`;
        }
        return this.request(fullEndpoint, 'GET', null, credentials, timeout, retries, useCache);
    }

    async post(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) {
        return this.request(endpoint, 'POST', body, credentials, timeout, retries);
    }

    async put(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) {
        return this.request(endpoint, 'PUT', body, credentials, timeout, retries);
    }

    async patch(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) {
        return this.request(endpoint, 'PATCH', body, credentials, timeout, retries);
    }

    async delete(endpoint, credentials = 'omit', timeout = 10000, retries = 3) {
        return this.request(endpoint, 'DELETE', null, credentials, timeout, retries);
    }

    useRequestInterceptor(interceptor) {
        this.requestInterceptors.push(interceptor);
    }

    useResponseInterceptor(interceptor) {
        this.responseInterceptors.push(interceptor);
    }

    cancelRequest(endpoint) {
        if (this.abortControllers.has(endpoint)) {
            this.abortControllers.get(endpoint).abort();
            this.abortControllers.delete(endpoint);
        }
    }

    clearCache() {
        this.cache.clear();
    }

    /**
     * 下载文件内容并解析为文本
     * @param {string} endpoint - 请求的端点
     * @param {Object} params - 查询参数
     * @param {string} credentials - 认证方式（如 'include' 或 'omit'）
     * @param {number} timeout - 超时时间
     * @param {number} retries - 重试次数
     * @returns {Promise<string>} - 返回文件内容的文本
     */
    async downloadText(endpoint, params = {}, credentials = 'omit', timeout = 10000, retries = 3) {
        let fullEndpoint = endpoint;
        if (Object.keys(params).length > 0) {
            const queryString = new URLSearchParams(params).toString();
            fullEndpoint += `?${queryString}`;
        }

        // 设置请求头，明确接收文本内容
        const headers = {
            ...this.headers,
            Accept: 'text/plain, text/html, text/csv', // 根据需要调整
        };

        // 发起请求并返回文本内容
        return await this._request(fullEndpoint, 'GET', null, credentials, timeout, {
            headers,
            responseType: 'text', // 明确返回文本
        });
    }

    /**
     * 下载文件并保存到本地
     * @param {string} endpoint - 请求的端点
     * @param {Object} params - 查询参数
     * @param {string} credentials - 认证方式（如 'include' 或 'omit'）
     * @param {number} timeout - 超时时间
     * @param {number} retries - 重试次数
     * @param {string} fileName - 保存文件的名称（可选）
     * @returns {Promise<void>}
     */
    async download(endpoint, params = {}, credentials = 'omit', timeout = 10000, retries = 3, fileName = null) {
        let fullEndpoint = endpoint;
        if (Object.keys(params).length > 0) {
            const queryString = new URLSearchParams(params).toString();
            fullEndpoint += `?${queryString}`;
        }

        const result = await this.request(fullEndpoint, 'GET', null, credentials, timeout, retries, false, {
            responseType: 'blob', // 告诉 fetch 返回 Blob 对象
        });

        this._saveFile(result, fileName);
    }

    /**
     * 保存文件到本地
     * @param {Blob} blob - 文件的 Blob 对象
     * @param {string} fileName - 保存文件的名称
     */
    _saveFile(blob, fileName) {
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName || 'download'; // 默认文件名为 'download'
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        window.URL.revokeObjectURL(url);
    }

    /**
     * 获取流式响应数据
     * @param {string} endpoint - 请求的端点
     * @param {Object} params - 查询参数
     * @param {Function} onData - 处理数据块的回调函数
     * @param {Function} onDone - 流式读取完成时的回调函数
     * @param {Function} onError - 错误处理回调函数
     * @param {string} credentials - 认证方式（如 'include' 或 'omit'）
     * @param {number} timeout - 超时时间
     * @returns {Promise<void>}
     */
    async getStream(endpoint, params = {}, onData, onDone, onError, credentials = 'omit', timeout = 10000) {
        let fullEndpoint = endpoint;
        if (Object.keys(params).length > 0) {
            const queryString = new URLSearchParams(params).toString();
            fullEndpoint += `?${queryString}`;
        }

        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);

        try {
            const response = await fetch(`${this.baseUrl}${fullEndpoint}`, {
                method: 'GET',
                headers: this.headers,
                credentials,
                signal: controller.signal,
            });

            if (!response.ok) {
                throw new ApiError(`HTTP 错误! 状态码: ${response.status}`, response.status, response);
            }

            const reader = response.body.getReader();
            const decoder = new TextDecoder('utf-8'); // 根据需要调整编码

            while (true) {
                const {done, value} = await reader.read();
                if (done) {
                    onDone(); // 流式读取完成
                    break;
                }
                const chunk = decoder.decode(value, {stream: true});
                onData(chunk); // 处理数据块
            }
        } catch (error) {
            onError(error); // 错误处理
        } finally {
            clearTimeout(timeoutId);
        }
    }

    /**
     * 上传文件
     * @param {string} endpoint - 请求的端点
     * @param {File|File[]} files - 要上传的文件（单个文件或文件数组）
     * @param {Object} formData - 额外的表单数据（可选）
     * @param {string} credentials - 认证方式（如 'include' 或 'omit'）
     * @param {number} timeout - 超时时间
     * @param {number} retries - 重试次数
     * @returns {Promise<any>} - 返回服务器响应
     */
    async upload(endpoint, files, formData = {}, credentials = 'omit', timeout = 10000, retries = 3) {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);

        // 创建 FormData 对象
        const data = new FormData();

        // 添加文件
        if (Array.isArray(files)) {
            files.forEach((file) => {
                data.append('files', file); // 字段名必须为 'files'
            });
        } else {
            data.append('file', files); // 字段名必须为 file
        }

        // 添加额外的表单数据
        for (const key in formData) {
            if (formData.hasOwnProperty(key)) {
                data.append(key, formData[key]);
            }
        }

        try {
            const response = await fetch(`${this.baseUrl}${endpoint}`, {
                method: 'POST',
                headers: {
                    // 不要手动设置 Content-Type，浏览器会自动处理
                },
                credentials,
                body: data,
                signal: controller.signal,
            });

            if (!response.ok) {
                throw new ApiError(`HTTP 错误! 状态码: ${response.status}`, response.status, response);
            }

            return await response.json();
        } catch (error) {
            console.error('上传失败:', error);
            throw error;
        } finally {
            clearTimeout(timeoutId);
        }
    }
}

class ApiError extends Error {
    constructor(message, statusCode, response, config) {
        super(message);
        this.statusCode = statusCode;
        this.response = response;
        this.config = config;
    }
}

export default Api;